/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package app.persistencia;

import app.modelo.Libro;
import app.negocio.LibroNoEncontradoException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Johann Pando
 */
public class LibrosDAO implements ItfzLibrosDAO {

    private Connection conexion;

    /**
     * {@inheritDoc }
     *
     * @param libro
     * @return insertOk
     */
    @Override
    public boolean altaLibro(Libro libro) {

        boolean insertOk = false;

        try {

            //Identiicador del libro que se incializa a 1 si es que no hay ningún registro.
            int idLibro = 1;

            //Consultamos la cantidad de registro haciendo uso del método consultarTodos();
            //List<Libro> numRegistros = consultarTodos();
            //Si existen registros sumamos el total más uno para definir el identificador.
            //if (numRegistros != null && !numRegistros.isEmpty()) {
            //    idLibro = numRegistros.size() + 1;
            //}                     
            //Abrimos la conexión a BD
            abrirConexion();

            String queryMaximoId = "select max(l.ID) as ID from APP.libros l";
            Statement st = conexion.createStatement();
            ResultSet rss = st.executeQuery(queryMaximoId);
            while (rss.next()) {
                idLibro = rss.getInt("ID") + 1;
            }
            rss.close();

            //Preparamos la query que insertará el libro
            String query = "insert into APP.libros values (?, ?, ?, ?, ?, ?, ?, ?)";
            PreparedStatement ps = conexion.prepareStatement(query);

            //Sustituímos los parámetros
            ps.setInt(1, idLibro);
            ps.setString(2, libro.getTitulo());
            ps.setString(3, libro.getAutor());
            ps.setString(4, libro.getEditorial());
            ps.setString(5, libro.getIsbn());
            ps.setInt(6, libro.getPublicacion());
            ps.setDouble(7, libro.getPrecio());
            ps.setString(8, libro.getDescripcion());

            //Ejecutamos la query
            // Si executeUpdate devuelve 1 es que ha realizado la inserción correctamente.
            if (ps.executeUpdate() == 1) {
                insertOk = true;
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            //Cerramos la conexión
            cerrarConexion();
        }

        return insertOk;
    }

    @Override
    public boolean eliminarLibro(int id) {

        //Variable creada para verificar que se ha podido eliminar el libro.
        boolean libroEliminado = false;

        try {

            //Abrimos la conexión a BD
            abrirConexion();

            //Creamos la query que actualizará el precio del libro.
            String query = "delete from APP.libros l where l.id = ? ";
            PreparedStatement ps = conexion.prepareStatement(query);

            //Sustituímos los parámetros
            ps.setInt(1, id);

            //Ejecutamos la query
            // Si executeUpdate devuelve 1 es que ha realizado la eliminación correctamente.
            if (ps.executeUpdate() == 1) {
                libroEliminado = true;
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            //Cerramos la conexión a BD
            cerrarConexion();
        }

        return libroEliminado;
    }

    @Override
    public List<Libro> consultarTodos() {

        //Declaramos e instnciamos la variable que retornaremos.
        List<Libro> listaLibros = new ArrayList<>();
        try {

            //Abrimos la conexión
            abrirConexion();

            //Creamos la query que nos retornará todos los libros de la BD
            StringBuilder queryTodos = new StringBuilder();
            queryTodos.append("select * from APP.libros");
            Statement st = conexion.createStatement();
            //Ejecutamos la query
            ResultSet rs = st.executeQuery(queryTodos.toString());

            //Añadimos a la lista de libros los libros que haya devuelto la consulta a BD.
            anadeLibro(rs, listaLibros);

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            //Cerramos la conexión
            cerrarConexion();
        }
        return listaLibros;
    }

    @Override
    public Libro consultarISBN(String isbn) throws LibroNoEncontradoException {

        //Declaramos la variable de tipo libro a devolver.
        Libro libro = null;

        try {

            //Abrimos la conexión
            abrirConexion();

            //Creamos la query para eliminar el libro a través de su isbn
            String query = "select * from APP.libros l where l.isbn = ?";
            PreparedStatement ps = conexion.prepareStatement(query);

            //Sustituímos los parámetros
            ps.setString(1, isbn);

            //Ejecutamos la query
            ResultSet rs = ps.executeQuery();

            //el método anadeLibro nos devolverá una lista de libros.
            List<Libro> listaTemporal = new ArrayList<>();
            anadeLibro(rs, listaTemporal);

            //Si la ejecución ha devuelto resultados...
            if (!listaTemporal.isEmpty()) {
                //Devolvemos el primer elemnto (Debería haber devuelto un sólo valor)
                libro = listaTemporal.get(0);
            } else {
                // Si no se ha añadido nada en la lista lanzamos la exceción de libro encontrafo            
                throw new LibroNoEncontradoException();
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            //Cerramos la conexión
            cerrarConexion();
        }

        return libro;
    }

    @Override
    public List<Libro> consultarTitulo(String titulo) throws LibroNoEncontradoException {

        //Declaramos e instnciamos la variable que retornaremos.
        List<Libro> listaLibros = new ArrayList<>();

        try {
            //Abrimos la conexión a BD.
            abrirConexion();

            //Creamos la query a ejecutar.
            //Utilizamos la función upper para hacer la búsqueda en mayúsucula, 
            //de esta forma, el usuario no tendrá problemas al buscar en mayúscula o minúsucla.
            String query = "select * from APP.libros l where upper(l.titulo) like ?";
            PreparedStatement ps = conexion.prepareStatement(query);

            //Suistituímos los parámetros
            //Utilizamos la función upper para hacer la búsqueda en mayúsucula, 
            //de esta forma, el usuario no tendrá problemas al buscar en mayúscula o minúsucla.
            ps.setString(1, "%".concat(titulo.toUpperCase()).concat("%"));
            //ps.setString(1, titulo);//De esta forma no funciona.

            //Ejecutamos la query
            ResultSet rs = ps.executeQuery();

            //Añadimos a la lista de libros los libros que haya devuelto la consulta a BD.
            anadeLibro(rs, listaLibros);

            // Si no se ha añadido nada en la lista lanzamos la exceción de libro encontrafo            
            if (listaLibros.isEmpty()) {
                throw new LibroNoEncontradoException();
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        }

        return listaLibros;
    }

    @Override
    public boolean modificarPrecio(String isbn, double precio) {

        //Variable creada para verificar que se ha podido actualizar el precio del libro.
        boolean operacionCorrecta = false;

        try {

            //Abrimos la conexión a BD
            abrirConexion();

            //Creamos la query que actualizará el precio del libro.
            StringBuilder query = new StringBuilder();
            query.append("update APP.libros l set l.precio = ? ");
            query.append("where l.isbn = ?");
            PreparedStatement ps = conexion.prepareStatement(query.toString());

            //Sustituímos los parámetros
            ps.setDouble(1, precio);
            ps.setString(2, isbn);

            //Ejecutamos la query
            // Si executeUpdate devuelve 1 es que ha realizado la inserción correctamente.
            if (ps.executeUpdate() == 1) {
                operacionCorrecta = true;
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            //Cerramos la conexión a BD
            cerrarConexion();
        }

        return operacionCorrecta;
    }

    /**
     * M&eacute;todo que abre la conexi&oacute;n a BD.
     */
    public void abrirConexion() {
        try {

            //Carga el dirver de Derby.
            Class.forName("org.apache.derby.jdbc.ClientDriver");
            //Establece la conexión a nuestra base de datos.
            conexion = DriverManager.getConnection("jdbc:derby://localhost:1527/LIBRERIA", "curso", "curso");

        } catch (ClassNotFoundException | SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    /**
     * Método encargado de cerrar la conexi&oacute;n a BD.
     */
    public void cerrarConexion() {
        try {
            //Cierra la conexión
            conexion.close();
        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public List<String> cargarAutores() {
        List<String> listaAutores = new ArrayList<>();

        try {
            //Abrimos la conexión:
            abrirConexion();

            //Creamos la query para recupera los libros de un autor.
            String query = "select distinct l.autor from APP.libros l";
            Statement st = conexion.createStatement();
            ResultSet rs = st.executeQuery(query);

            if (rs != null) {
                while (rs.next()) {
                    listaAutores.add(rs.getString("AUTOR"));
                }
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            cerrarConexion();
        }

        return listaAutores;
    }

    @Override
    public List<Libro> cargaLibrosPorAutor(String autor) {
        List<Libro> listaLibros = new ArrayList<>();

        if (autor != null) {
            //Abrimos la conexión:
            abrirConexion();
            String query = "select * from app.libros l where l.AUTOR = ?";
            try {
                PreparedStatement ps = conexion.prepareStatement(query);
                ps.setString(1, autor.trim());

                ResultSet rs = ps.executeQuery();

                //Añadimos a la lista de libros los libros que haya devuelto la consulta a BD.
                anadeLibro(rs, listaLibros);

            } catch (SQLException ex) {
                Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
            }finally{
                cerrarConexion();
            }
        }

        return listaLibros;

    }

    @Override
    public List<String> cargarEditoriales() {
        List<String> listaEditoriales = new ArrayList<>();

        try {
            //Abrimos la conexión:
            abrirConexion();

            //Creamos la query para recupera los libros de un autor.
            String query = "select distinct l.editorial from APP.libros l";
            Statement st = conexion.createStatement();
            ResultSet rs = st.executeQuery(query);

            if (rs != null) {
                while (rs.next()) {
                    listaEditoriales.add(rs.getString("EDITORIAL"));
                }
            }

        } catch (SQLException ex) {
            Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            cerrarConexion();
        }

        return listaEditoriales;
    }
    
    @Override
    public List<Libro> cargaLibrosPorEditorial(String editorial) {
        List<Libro> listaLibros = new ArrayList<>();

        if (editorial != null) {
            //Abrimos la conexión:
            abrirConexion();
            String query = "select * from app.libros l where l.EDITORIAL = ?";
            try {
                PreparedStatement ps = conexion.prepareStatement(query);
                ps.setString(1, editorial.trim());

                ResultSet rs = ps.executeQuery();

                //Añadimos a la lista de libros los libros que haya devuelto la consulta a BD.
                anadeLibro(rs, listaLibros);

            } catch (SQLException ex) {
                Logger.getLogger(LibrosDAO.class.getName()).log(Level.SEVERE, null, ex);
            }finally{
                cerrarConexion();
            }
        }

        return listaLibros;
    }

    /**
     * Método que recibe el conjunto de resultados y una lista. Dicho
     * m&eacute;todo se encarga de recorrer la lista devuelta por consulta a BD
     * y la añade al libro actualizando la lista por referencia.
     *
     * @param rs
     * @param listaLibros
     * @throws SQLException
     */
    private void anadeLibro(ResultSet rs, List<Libro> listaLibros) throws SQLException {
        //Mientras hayan resultados.
        while (rs.next()) {
            //Creamos una instancia de libro.
            Libro libro = new Libro();
            //Ahora añadimos cada campo del libro
            libro.setID(rs.getInt("ID"));
            libro.setTitulo(rs.getString("TITULO"));
            libro.setAutor(rs.getString("AUTOR"));
            libro.setEditorial(rs.getString("EDITORIAL"));
            libro.setIsbn(rs.getString("ISBN"));
            libro.setPublicacion(rs.getInt("PUBLICACION"));
            libro.setPrecio(rs.getDouble("PRECIO"));
            libro.setDescripcion(rs.getString("DESCRIPCION"));
            //Por último añadimos a la lista del libro el libro actual.
            listaLibros.add(libro);
        }
    }

    public Connection getConexion() {
        return conexion;
    }

    public void setConexion(Connection conexion) {
        this.conexion = conexion;
    }
}
